package cc.wanforme.st.server.biz.service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import cc.wanforme.st.server.authen.dto.AdminRoleAuth;
import cc.wanforme.st.server.authen.dto.UserRoleAuth;
import cc.wanforme.st.server.authen.filter.MRememberMeFilter;
import cc.wanforme.st.server.authen.service.AuthService;
import cc.wanforme.st.server.authen.service.TokenService;
import cc.wanforme.st.server.base.entity.AuthToken;
import cc.wanforme.st.server.base.entity.RolePermission;
import cc.wanforme.st.server.base.entity.User;
import cc.wanforme.st.server.base.entity.UserRole;
import cc.wanforme.st.server.base.constant.UserType;
import cc.wanforme.st.server.base.entity.Admin;
import cc.wanforme.st.server.base.entity.AdminRole;
import cc.wanforme.st.server.base.service.IRolePermissionService;
import cc.wanforme.st.server.base.service.IUserRoleService;
import cc.wanforme.st.server.base.service.IUserService;
import cc.wanforme.st.server.base.service.IAdminRoleService;
import cc.wanforme.st.server.base.service.IAdminService;
import cc.wanforme.st.server.util.JwtUtil;
import cc.wanforme.st.server.util.PasswordUtil;
import cc.wanforme.st.server.util.encryt.Base64Tool;
import cc.wanforme.st.server.vo.ResMessage;
import cc.wanforme.st.server.vo.base.RolePermissionVo;
import cc.wanforme.st.server.vo.base.AdminVo;
import cc.wanforme.st.server.vo.login.LoginVo;
import cc.wanforme.st.server.vo.user.AdminTokenVo;
import cc.wanforme.st.server.vo.user.UserTokenVo;

@Service
public class LoginService {
	@Autowired
	private TokenService tokenService;
	@Autowired
	private AuthService authService;
	@Autowired
	private IAdminService adminService;
	@Autowired
	private IUserService userService;
	@Autowired
	private IAdminRoleService adminRoleService ;
	@Autowired
	private IUserRoleService userRoleService;
	@Autowired
	private IRolePermissionService rolePermissionService ;
	
	/** 管理员登录
	 * @param vo
	 * @param isEncData 是否是加密数据（使用 Base64Tool 解密）。
	 * @param request
	 * @param response
	 * @return 返回 token
	 */
	public ResMessage<AdminTokenVo> login(LoginVo vo, boolean isEncData,
			HttpServletRequest request, HttpServletResponse response) {
		String username = vo.getUsername();
		String password = vo.getPassword();

		Assert.notNull(username, "未输入用户名");
		Assert.notNull(password, "未输入密码");

		if(isEncData) {
			username = Base64Tool.decodeReqData(username);
			password = Base64Tool.decodeReqData(password);	
		}
		
		Admin admin = adminService.selectByUsername(username);
		if(admin == null || !PasswordUtil.checkPassword(password, admin.getPassword())) {
			return ResMessage.fail("密码或用户名错误");
		}
		
		// 生成并保存 token
		AuthToken token = tokenService.generateAndSaveToken(admin.getId(), admin.getUsername(),
				UserType.ADMIN, request, response);
		
		// 清除并重新设置验证信息
		// tokenService.clearAuthentications(response);
		AdminRoleAuth roleAuth = adminService.selectAuthenticationsByAdmin(token.getUserId());
		authService.setAuthentications(roleAuth);
		
		AdminTokenVo adminVo = new AdminTokenVo();
		BeanUtils.copyProperties(admin, adminVo);
		adminVo.setToken(token.getToken());
		
		return ResMessage.success("登录成功", adminVo);
	}
	
	public ResMessage<AdminTokenVo> currentAdminInfo(HttpServletRequest request, HttpServletResponse response) {
		String token = MRememberMeFilter.getToken(request, tokenService);
		Admin admin = adminService.selectAdminByToken(token);
		
		AdminTokenVo adminVo = new AdminTokenVo();
		BeanUtils.copyProperties(admin, adminVo);
		adminVo.setToken(token);
		
		return ResMessage.success("获取成功", adminVo);
	}
	
	public ResMessage<UserTokenVo> currentUserInfo(HttpServletRequest request, HttpServletResponse response) {
		String token = MRememberMeFilter.getToken(request, tokenService);
		User user = userService.selectUserByToken(token);
		
		UserTokenVo userVo = new UserTokenVo();
		BeanUtils.copyProperties(user, userVo);
		userVo.setToken(token);
		
		return ResMessage.success("获取成功", userVo);
	}
	
	/** 退出 */
	public ResMessage<Void> logout(HttpServletRequest request, HttpServletResponse response) {
		tokenService.clearAuthentications(response);
		// 清除缓存
		String token = MRememberMeFilter.getToken(request, tokenService);
		tokenService.clearCacheToken(token);
		
		// 重置数据库
		AuthToken authToken = tokenService.selectValidToken(token);
		if(authToken!=null) {
			authToken.setExpireTime(LocalDateTime.now());
			tokenService.updateToken(authToken);
		}
		
		return ResMessage.success("退出成功", null);
	}
	
	/** 查询当前登录的所有权限，不会从数据库查，直接从 SecurityContext 中获取，可能拿不到最新的数据
	 * @return
	 */
	@Deprecated
	public ResMessage<Map<String, Object>> currentPermession(HttpServletRequest request) {
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
		List<String> list = new ArrayList<>(0);
		if( authorities!=null ) {
			list = authorities.stream().map(GrantedAuthority::getAuthority)
			.collect(Collectors.toList());
		}
		
		if( this.checkGuest(auth) ) {
			return ResMessage.success("", null);
		}
		
		String principal = auth.getPrincipal().toString();
		Admin admin = adminService.selectByUsername(principal);
		if(admin==null) {
			return ResMessage.fail("信息已过期，请重新登陆！", null);
		}
		
		AdminVo adminVo = new AdminVo();
		BeanUtils.copyProperties(admin, adminVo);
		
		Map<String, Object> data = new HashMap<>(2);
		data.put("info", adminVo);
		data.put("auth", list);
		return ResMessage.success("查询成功", data);
	}

	/** 查询当前登录的所有权限，不会从数据库查
	 * @return
	 */
	public ResMessage<List<RolePermissionVo>> tokenPermessions(HttpServletRequest request) {
		String token = MRememberMeFilter.getToken(request, tokenService);
		
		List<RolePermissionVo> reList = new ArrayList<>();
		String type = JwtUtil.getType(token);
		
		if(UserType.ADMIN.name().equals(type)) {
			reList = this.adminPermissions(token);
		} else if(UserType.USER.name().equals(type)) {
			reList = this.userPermissions(token);
		}

		return ResMessage.success("查询成功", reList);
	}
	
	/** 管理员的权限
	 * @param token
	 * @return
	 */
	private List<RolePermissionVo> adminPermissions(String token) {
		List<RolePermissionVo> reList = new ArrayList<>();
		Admin admin = adminService.selectAdminByToken(token);
		
		AdminTokenVo adminVo = new AdminTokenVo();
		BeanUtils.copyProperties(admin, adminVo);
		adminVo.setToken(token);
		
		// 所有角色
		List<AdminRole> adminRoles = adminRoleService.selectRoles(admin.getId());
		if(adminRoles!=null && !adminRoles.isEmpty()) {
			for (AdminRole ur : adminRoles) {
				RolePermissionVo rpVo = new RolePermissionVo();
				rpVo.setRole(ur.getRole());
				
				// 角色的权限
				List<RolePermission> plist = rolePermissionService.selectPermissions(ur.getRole());
				if(plist!=null && !plist.isEmpty()) {
					List<String> collect = plist.stream()
							.map(RolePermission::getPermission)
							.collect(Collectors.toList());
					rpVo.setPermissions(collect);
				}
				
				reList.add(rpVo);
			}
		}
		return reList;
	}
	
	/** 用户的权限
	 * @param token
	 * @return
	 */
	private List<RolePermissionVo> userPermissions(String token) {
		User user = userService.selectUserByToken(token);
		List<RolePermissionVo> reList = new ArrayList<>();
		
		UserTokenVo userVo = new UserTokenVo();
		BeanUtils.copyProperties(user, userVo);
		userVo.setToken(token);
		
		// 所有角色
		List<UserRole> userRoles = userRoleService.selectRoles(user.getId());
		if(userRoles!=null && !userRoles.isEmpty()) {
			for (UserRole ur : userRoles) {
				RolePermissionVo rpVo = new RolePermissionVo();
				rpVo.setRole(ur.getRole());
				
				// 角色的权限
				List<RolePermission> plist = rolePermissionService.selectPermissions(ur.getRole());
				if(plist!=null && !plist.isEmpty()) {
					List<String> collect = plist.stream()
							.map(RolePermission::getPermission)
							.collect(Collectors.toList());
					rpVo.setPermissions(collect);
				}
				
				reList.add(rpVo);
			}
		}
		return reList;
	}
	
	private boolean checkGuest(Authentication auth) {
		String principal = auth.getPrincipal().toString(); // anonymousUser
		Object credentials = auth.getCredentials(); // 空字符串
		Collection<? extends GrantedAuthority> authorities2 = auth.getAuthorities(); // [ROLE_ANONYMOUS]
//		System.out.println(principal+credentials);
//		System.out.println(authorities2.toString());
		
		return "anonymousUser[ROLE_ANONYMOUS]".equals(principal+credentials+authorities2.toString());
	}

	/** 从http请求中获取当前登录的用户id
	 * @param request
	 * @return
	 */
	public Long currentId(HttpServletRequest request) {
		String token = MRememberMeFilter.getToken(request, tokenService);
		AuthToken authToken = tokenService.selectValidToken(token);
		return authToken==null ? null : authToken.getUserId();
	}
	
	/** 从http请求中获取当前登录用户
	 * @param request
	 * @return
	 */
	public Admin currentAdmin(HttpServletRequest request) {
		Long adminId = this.currentId(request);
		if(adminId==null) {
			return null;
		}
		
		Admin admin = adminService.getById(adminId);
		return admin;
	}
	
	/** 当前用户，从 Spring 的授权认证中获取当前用户
	 * @return
	 */
	public Admin currentAdmin() {
		Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		//System.out.println("principal: " + principal.getClass() + ", " + principal);
		return (Admin)principal;
	}
	
	/** 普通用户登录
	 * @param vo
	 * @param isEncData 是否是加密数据（使用 Base64Tool 解密）。
	 * @param request
	 * @param response
	 * @return 返回 token
	 */
	public ResMessage<UserTokenVo> signUp(LoginVo vo, boolean isEncData,
			HttpServletRequest request, HttpServletResponse response) {
		String username = vo.getUsername();
		String password = vo.getPassword();

		Assert.notNull(username, "未输入用户名");
		Assert.notNull(password, "未输入密码");

		if(isEncData) {
			username = Base64Tool.decodeReqData(username);
			password = Base64Tool.decodeReqData(password);	
		}
		
		User user = userService.selectByUsername(username);
		
		if(user == null || !PasswordUtil.checkPassword(password, user.getPassword())) {
			return ResMessage.fail("密码或用户名错误");
		}
		
		// 生成并保存 token
		AuthToken token = tokenService.generateAndSaveToken(user.getId(), user.getUsername(),
				UserType.ADMIN, request, response);
		
		// 清除并重新设置验证信息
		// tokenService.clearAuthentications(response);
		UserRoleAuth userRoleAuth = userService.selectAuthenticationsByUser(token.getUserId());
		authService.setAuthentications(userRoleAuth);
		
		UserTokenVo userVo = new UserTokenVo();
		BeanUtils.copyProperties(user, userVo);
		userVo.setToken(token.getToken());
		
		return ResMessage.success("登录成功", userVo);
	}

}
